-
Notifications
You must be signed in to change notification settings - Fork 724
feat: new extension API WithExecCommand
#10779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a new extension API WithExecCommand that allows registering commands to execute inside container resources. The feature enables executing commands like ls directly within containers and streaming the output back as logs, similar to existing WithCommand but specifically designed for container execution scenarios.
Key changes:
- New
WithExecCommandextension API for registering container execution commands ContainerExecServicefor executing commands and streaming output from containers- Extended
IDcpExecutorinterface to support ephemeral resource execution and cleanup
Reviewed Changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/Aspire.Hosting/ResourceBuilderExtensions.cs |
Adds the new WithExecCommand extension method |
src/Aspire.Hosting/ApplicationModel/ResourceContainerExecCommandAnnotation.cs |
New annotation class for container exec commands and output model |
src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotationBase.cs |
New base class extracting common command annotation functionality |
src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs |
Refactored to inherit from new base class |
src/Aspire.Hosting/Exec/ContainerExecService.cs |
New service for executing container commands and streaming output |
src/Aspire.Hosting/Dcp/IDcpExecutor.cs |
Extended interface to support ephemeral resource operations |
src/Aspire.Hosting/Dcp/DcpExecutor.cs |
Implementation of ephemeral resource execution and cleanup |
src/Aspire.Hosting/Dcp/DcpResourceState.cs |
Added methods for managing ephemeral resources in state |
src/Aspire.Hosting/DistributedApplicationBuilder.cs |
Registered ContainerExecService in DI container |
| Various backchannel files | Renamed CommandOutput to BackchannelCommandOutput for clarity |
| Test files | Added comprehensive tests for the new functionality |
src/Aspire.Hosting/ApplicationModel/ResourceContainerExecCommandAnnotation.cs
Outdated
Show resolved
Hide resolved
src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs
Outdated
Show resolved
Hide resolved
| /// <param name="command">The command string to be executed.</param> | ||
| /// <param name="commandOptions">Optional settings for the command, such as description and icon.</param> | ||
| /// <returns>The resource builder, allowing for method chaining.</returns> | ||
| public static IResourceBuilder<T> WithExecCommand<T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about ExecCommand as the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as per requirement in the issue (see #10301), it should be with the name.
It allows to add multiple commands per name to the same container resource. for example
builder
.AddResource(containerResource)
.WithExecCommand("listfiles", ...)
.WithExecCommand("deletefiles", ...)
Why are you not sure about such API? It is basically a copy-cat from WithCommand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open to suggestions RE naming. This follows the pattern established by WithHttpCommand.
|
(from the PR description)
Not sure what you mean. The status for |
karolz-ms
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main concern is the interface for executing a container command, it needs to be re-designed IMO.
|
|
||
| if (ephemeralResource.ModelResource is ContainerExecutableResource) | ||
| { | ||
| _resourceState.Remove(ephemeralResource); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the resource be absent from either of these two lists? If so, what should happen?
Is it enough to just remove the resource from these two lists without doing any additional cleanup? Like, stopping log streams associated with the resource? If the answer is "yes, it is sufficient, no extra cleanup is needed", then at least it is worth putting a comment here explaining WHY
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've included a comment on the collection addition/removal. Those collections are a part of DcpExecutor to enable correct monitoring of resource state and log collection. It is an obscure part of DcpExecutor and it needs a refactor to have more convenient API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is that comment? I do not see any in the latest version of DeleteEphemeralResourceAsync() method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are RunEphemeralResourceAsync() and DeleteEphemeralResourceAsync() methods tread-safe? (I assume not) What are the assumptions we are making about the context from which they will be called?
|
OK, so I've reviewed this code. I definitely do not want us to introduce What we should be doing is in the We should look to One thing that we should probably think carefully about here is whether it actually makes sense to define container exec commands in the app model as a new kind of resource. Presently we have This way we might be able to take advantage of all of the plumbing we already have in Aspire around handling log streaming etc. I believe that |
|
@DeagleGross please look to address some of the feedback I've added as comments to this commit in this PR: If we are introducing a resource into the app model and we implement interfaces like Additionally instead of having a |
|
@mitchdenny thanks for the explanation about similarities with However, I've refactored the code and removed the base annotation class etc, so its cleaner now. The cli changes for |
|
|
||
| Func<CancellationToken, Task<ExecuteCommandResult>> commandResultTask = async (CancellationToken cancellationToken) => | ||
| { | ||
| await _dcpExecutor.RunEphemeralResourceAsync(containerExecResource, cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems awkward that the call to ContainerExecService.ExecuteCommand() does not, in fact, executes the command.
Consider making this method an async method and taking the call to RunEphemenralResourceAsync() out of the result-producing task. This will guarantee that when ExecuteCommandCore() returns without exception, the command is started. The returned run object would then expose GetResultAsync() method for waiting on the result.
This change also guarantees that calling GetOutputStream() on the returned run object always makes sense. Without it, it is possible to ask for output stream without actually starting the command, which will result in waiting forever (until the cancellation token expires)
I think there might be a bit of a disconnect here. When I look at With that in mind I'm wondering if we are conflating two things here - commanding on the dashboard, and the introduction of a sub-resource for containers capable of executing resources within a running container. If we imagine that dashboard commands don't exist for a moment, the way we might add this feature is that we would do something like this: var builder = DistributedApplication.CreateBuilder(args);
builder.AddContainer("mycontainer", "myimage")
.AddInvocation("truncate-database", "dbtool", "truncate");Note I'm using What the code above would do is that the container would start, and there would be an implicit WaitFor(...) on the parent resource before the Now - the interesting here is how this maps onto This doesn't really have anything to do with dashboard commands - in fact they aren't even really required. Because You could - if you wanted to also hide the resource so that it is not alway visible and then add a command using That |
|
@mitchdenny and @DeagleGross, just wanted to point out that I guess how we incorporate this capability into Aspire model is up for debate. We need to consider deployment environments which may or may not have that capability for containers. Personally, I am not very sure if modeling "execution of programs within containers" (avoiding the overloaded term "command" here) is a good candidate for being a distinct resource type in Aspire model. I am wondering if something more lightweight (e.g. just a method on the container resource) would ultimately be easier to use and more appropriate. |
This is exactly my thinking too. I do not see this as a resource at all, but rather exposing the ability to execute/invoke something in the context of a resource. |
|
My concern is that if we don't model this as a resource from top to bottom then we are going to end up with unnecessary duplications for something that conceptually is very well aligned with resources in the Aspire app model. I mean we define models in Ollama as resources - why not a |
|
This submission has been automatically marked as stale because it has been marked as requiring author action but has not had any activity for 14 days. |
Description
PR is an implementation of
WithExecCommandextension (seeIn Aspire.Hostingin #10301), which allows to register a command to execute against a container resource (actually inside the container - for examplels(list files) in thenginxcontainer as is shown in tests).WithExecCommandis similar toWithCommandbecause it annotates a container resource with the newResourceContainerExecCommandAnnotation. It is very similar to existingResourceCommandAnnotation.There has to be a way to actually launch a command and it will be implemented in the follow-up PR for #10301, part
In the dashboard), and newContainerExecServiceexposes an API to do it.WithCommandhas different semantics: since the "command" is basically a c# callback, we can know if execution of the "command" has completed successfully, and we can return it. In case of containers there is no reliable way to know how the command execution went, and the most important here is to fetch logs, so I've built API which returns anIAsyncEnumerable<ContainerExecCommandOutput>- basically a stream of logs.On the opposite of the
WithCommandhere it is necessary to run the theContainerExecinside of DCP to actually execute the command inside the container. See implementation details below.Public API changes
Implementation
I decided to allow
IDcpExecutorto do 2 operations:interface IDcpExecutor { + Task<AppResource> RunEphemeralResourceAsync(IResource ephemeralResource, CancellationToken cancellationToken); + void DeleteEphemeralResource(AppResource? ephemeralResource); }The idea is that we dont know what custom commands will be executed against the container. In #10301 we will also support running custom commands from the dashboard via the interaction with the user. So we not always have an opportunity to register the
ContainerExecutableResourcebefore DCP starts, so I've extended the possibilities ofDcpExecutorto run a new resource and to delete the handlers to it (only for monitoring purposes). I knowDcpExecutorwas an immutable entity before, so this can be a concerning change.Checklist
<remarks />and<code />elements on your triple slash comments?